package com.tanhua.dubbo.server.api;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.PageUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.dubbo.common.utils.StringUtils;
import com.alibaba.dubbo.config.annotation.Service;
import com.tanhua.dubbo.server.enums.FreezingRangeEnum;
import com.tanhua.dubbo.server.vo.IdType;
import com.tanhua.dubbo.server.pojo.*;
import com.tanhua.dubbo.server.service.IdService;
import com.tanhua.dubbo.server.service.TimeLineService;
import com.tanhua.dubbo.server.vo.CommentType;
import com.tanhua.dubbo.server.vo.PageInfo;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.List;

@Service(version = "1.0.0")
@Slf4j
public class QuanZiApiImpl implements QuanZiApi {

    @Autowired
    private ManageFreezeApiImpl manageFreezeApi;

    //评论数据存储在Redis中key的前缀
    private static final String COMMENT_REDIS_KEY_PREFIX = "QUANZI_COMMENT_";

    //用户是否点赞的前缀
    private static final String COMMENT_USER_LIKE_REDIS_KEY_PREFIX = "USER_LIKE_";

    //用户是否喜欢的前缀
    private static final String COMMENT_USER_LOVE_REDIS_KEY_PREFIX = "USER_LOVE_";

    public String getCommentRedisKeyPrefix(String publishId) {
        return COMMENT_REDIS_KEY_PREFIX + publishId;
    }

    public String getCommentUserLikeRedisKeyPrefix(Long userId) {
        return COMMENT_USER_LIKE_REDIS_KEY_PREFIX + userId;
    }

    //TODO
    public String getCommentUserLoveRedisKeyPrefix(Long userId) {
        return COMMENT_USER_LOVE_REDIS_KEY_PREFIX + userId;
    }


    @Autowired
    private MongoTemplate mongoTemplate;
    @Autowired
    RedisTemplate<String, String> redisTemplate;
    @Autowired
    private IdService idService;
    @Autowired
    private TimeLineService timeLineService;


    /**
     * 实现功能:获取所有好友发布的动态信息
     *
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Publish> friendQuanZiPublish(Long userId, Integer page, Integer pageSize) {
        PageInfo<Publish> pageInfo = new PageInfo<>();
        pageInfo.setPageSize(pageSize);
        pageInfo.setPageNum(page);
        try {
            //1.根据时间线查询所有的好友信息
            PageRequest pageRequest = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.desc("date")));
            Query query = new Query().with(pageRequest);
            List<TimeLine> timeLines = mongoTemplate.find(query, TimeLine.class, "quanzi_time_line_" + userId);
            if (CollUtil.isEmpty(timeLines)) {
                return pageInfo;
            }
            //2.从动态信息里面查找所有的时间线里面好友发布的动态
            List<Object> publishId = CollUtil.getFieldValues(timeLines, "publishId");
            Query query2 = Query.query(Criteria.where("id").in(publishId)).with(Sort.by(Sort.Order.desc("created")));
            List<Publish> publishes = mongoTemplate.find(query2, Publish.class);
            pageInfo.setRecords(publishes);
            return pageInfo;
        } catch (Exception e) {
            log.error("获取好友的动态信息错误!");
            return pageInfo;
        }
    }

    /**
     * 实现功能:获取所有系统推荐的动态信息
     *
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Publish> recommendedQuanZiPublish(Long userId, Integer page, Integer pageSize) {
        PageInfo<Publish> pageInfo = new PageInfo<>();
        pageInfo.setPageSize(pageSize);
        pageInfo.setPageNum(page);
        try {
            //1.查询系统推荐的所有的动态id
            String redisKey = "QUANZI_PUBLISH_RECOMMEND_" + userId;
            String idsStr = redisTemplate.opsForValue().get(redisKey);
            if (StringUtils.isEmpty(idsStr)) {
                return pageInfo;
            }
            List<String> pIds = StrUtil.split(idsStr, ',');
            int[] ints = PageUtil.transToStartEnd(page - 1, pageSize);
            ArrayList<Long> pIdList = new ArrayList<>();
            for (int i = ints[0]; i < Math.min(ints[1], pIds.size()); i++) {
                pIdList.add(Long.valueOf(pIds.get(i)));
            }
            //2.从动态信息里面查找所有的系统推荐的动态,按照动态的创建时间倒序排序
            Query query = Query.query(Criteria.where("pid").in(pIdList)).with(Sort.by(Sort.Order.desc("created")));
            List<Publish> publishes = mongoTemplate.find(query, Publish.class);

            //获取所有审核通过的动态信息
            List<Publish> publishList = new ArrayList<>();
            for (Publish publish : publishes) {
                if (publish.getState() == 5) {
                    publishList.add(publish);
                }
            }
            publishes = publishList;


            pageInfo.setRecords(publishes);
            return pageInfo;
        } catch (Exception e) {
            log.error("获取好友的动态信息错误!");
            return pageInfo;
        }
    }

    /**
     * 发布动态
     *
     * @param userId
     * @param publish
     * @return
     */
    @Override
    public String savePublish(Long userId, Publish publish) {
        //判断是否冻结，以及冻结范围是否是冻结发言
        Boolean result = this.manageFreezeApi.isAccountState(Convert.toInt(userId), FreezingRangeEnum.DYNAMIC);
        if (result) {
            return null;
        }


        if (!ObjectUtil.isAllNotEmpty(userId, publish.getText())) {
            return null;
        }
        publish.setId(ObjectId.get());
        publish.setCreated(System.currentTimeMillis());
        publish.setPid(idService.creatIncrement(IdType.PUBLISH));
        //保存动态信息
        mongoTemplate.save(publish);
        //写入相册表
        Album album = new Album();
        album.setId(ObjectId.get());
        album.setCreated(System.currentTimeMillis());
        album.setPublishId(publish.getId());
        mongoTemplate.save(album, "quanzi_album_" + userId);
        //异步写入时间线
        timeLineService.saveTimeLine(userId, publish.getId());
        return publish.getId().toHexString();
    }

    /**
     * 实现功能:点赞动态,评论,或者喜欢动态成功或失败
     *
     * @param publishId
     * @param like
     * @return
     */
    @Override
    public boolean likeAndLove(Long userId, String publishId, CommentType like, String content, IdType type) {
        //1.判断用户是否已经喜欢或者点赞了
        if (this.judgeUserLikeAndLove(userId, publishId, like)) {
            return false;
        }
        //2.保存点赞的comment
        //TODO
        Boolean result = this.saveLikeAndLoveAndComment(userId, publishId, like, content, type);
        if (!result) {
            return false;
        }
        //3.写入redis缓存中,修改redis缓存中的数量
        String redisKey = getCommentRedisKeyPrefix(publishId);
        String likeKey = getCommentUserLikeRedisKeyPrefix(userId);
        if (like != CommentType.LIKE) {
            likeKey = getCommentUserLoveRedisKeyPrefix(userId);
        }
        redisTemplate.opsForHash().put(redisKey, likeKey, "1");
        String likeKeyCount = like.toString();
        redisTemplate.opsForHash().increment(redisKey, likeKeyCount, 1);
        return true;
    }

    /**
     * 保存点赞或者喜欢,评论的功能
     *
     * @param userId
     * @param publishId
     * @param like
     * @param content
     * @return
     */
    public Boolean saveLikeAndLoveAndComment(Long userId, String publishId, CommentType like, String content, IdType type) {
        //判断是否冻结，以及冻结范围是否是冻结发言
        Boolean result = this.manageFreezeApi.isAccountState(Convert.toInt(userId),FreezingRangeEnum.MAKE);
        if (result) {
            return false;
        }
        try {
            //1.创建comment对象
            Comment comment = new Comment();
            comment.setId(ObjectId.get());
            comment.setUserId(userId);
            comment.setPublishId(new ObjectId(publishId));
            comment.setCommentType(like.getType());
            comment.setContent(content);
            comment.setCreated(System.currentTimeMillis());
            //设置发布id
            comment.setPublishUserId(getPublish(publishId, type));
            //保存comment对象
            mongoTemplate.save(comment);
            return true;
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 统计点赞或者喜欢,评论的数量
     *
     * @param publishId
     * @param like
     * @return
     */
    @Override
    public Long likeAndLoveCount(Long userId, String publishId, CommentType like) {
        //1.先看缓存中是否有数据
        String redisKey = getCommentRedisKeyPrefix(publishId);
        String likeKeyCount = like.toString();
        Object object = redisTemplate.opsForHash().get(redisKey, likeKeyCount);
        if (ObjectUtil.isNotEmpty(object)) {
            return Long.valueOf(Convert.toStr(object));
        }
        //2.从MongoDB中查找
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId)).and("commentType").is(like.getType()));
        long count = mongoTemplate.count(query, Comment.class);
        //3.讲结果写入到redis缓存中
        redisTemplate.opsForHash().put(redisKey, likeKeyCount, count + "");
        return count;
    }

    /**
     * 确定用户是否点赞,喜欢动态或者评论
     *
     * @param userId
     * @param publishId
     * @param like
     * @return
     */
    @Override
    public boolean judgeUserLikeAndLove(Long userId, String publishId, CommentType like) {
        //1.从redis缓存中获取是否点赞
        String redisKey = getCommentRedisKeyPrefix(publishId);
        String likeKey;
        if (like == CommentType.LIKE) {
            likeKey = getCommentUserLikeRedisKeyPrefix(userId);
        } else {
            likeKey = getCommentUserLoveRedisKeyPrefix(userId);
        }
        Object object = redisTemplate.opsForHash().get(redisKey, likeKey);
        if (ObjectUtil.isNotEmpty(object)) {
            return StringUtils.isEquals(Convert.toStr(object), "1");
        }
        //2.再从MongoDB中获取是否点赞
        Query query = Query.query(Criteria.where("userId").is(userId).
                and(publishId).is(new ObjectId(publishId)).and("commentType").is(like.getType()));
        long count = mongoTemplate.count(query, Comment.class);
        if (count == 0) {
            return false;
        }
        //3.如果redis缓存中没有确定点赞的数据,MongoDB中也点赞的数据,将redis缓存中的状态修改成点赞的状态
        redisTemplate.opsForHash().put(redisKey, likeKey, "1");
        return true;
    }


    /**
     * 实现功能:点赞动态,评论,或者喜欢动态成功或失败(取消)
     *
     * @param publishId
     * @param like
     * @return
     */
    @Override
    public boolean cancelLikeAndLove(Long userId, String publishId, CommentType like, String content, IdType type) {
        //1.判断用户是否已经喜欢或者点赞了
        if (!this.judgeUserLikeAndLove(userId, publishId, like)) {
            return false;
        }
        //2.取消点赞的comment
        Boolean result = this.deleteLikeAndLoveAndComment(userId, publishId, like);
        if (!result) {
            return false;
        }
        //3.写入redis缓存中,修改redis缓存中的数量
        String redisKey = getCommentRedisKeyPrefix(publishId);
        String likeKey = getCommentUserLikeRedisKeyPrefix(userId);
        if (like != CommentType.LIKE) {
            likeKey = getCommentUserLoveRedisKeyPrefix(userId);
        }
        redisTemplate.opsForHash().delete(redisKey, likeKey);
        String likeKeyCount = like.toString();
        redisTemplate.opsForHash().increment(redisKey, likeKeyCount, -1);
        return true;
    }

    /**
     * 查询单条动态
     *
     * @param publishId
     * @return
     */
    @Override
    public Publish getOnePublish(String publishId) {
        return mongoTemplate.findById(new ObjectId(publishId), Publish.class);
    }

    /**
     * 获取指定动态的评论信息
     *
     * @param publishId
     * @return
     */
    @Override
    public List<Comment> findAllComments(Integer page, Integer pageSize, String publishId) {
        //创建符合查询条件的数据
        PageRequest pageRequest = PageRequest.of(page - 1, pageSize, Sort.by(Sort.Order.desc("created")));
        Query query = Query.query(Criteria.where("publishId").is(new ObjectId(publishId)).and("commentType").is(CommentType.COMMENT.getType())).with(pageRequest);
        //返回数据
        return mongoTemplate.find(query, Comment.class);
    }

    /**
     * 根据用户id获取用户所有的动态
     *
     * @param userId
     * @param page
     * @param pageSize
     * @return
     */
    @Override
    public PageInfo<Publish> meQuanZiPublish(Long userId, Integer page, Integer pageSize) {
        PageInfo<Publish> pageInfo = new PageInfo<Publish>();
        pageInfo.setPageSize(pageSize);
        pageInfo.setPageNum(page);
        Query query = Query.query(Criteria.where("userId").is(userId)).with(Sort.by(Sort.Order.desc("created")));
        List<Publish> publishes = mongoTemplate.find(query, Publish.class);
        //获取所有审核通过的动态信息
        List<Publish> publishList = new ArrayList<>();
        for (Publish publish : publishes) {
            if (publish.getState() == 5) {
                publishList.add(publish);
            }
        }
        publishes = publishList;

        pageInfo.setRecords(publishes);
        Query queryCount = Query.query(Criteria.where("userId").is(userId));
        long count = mongoTemplate.count(queryCount, Publish.class);
        pageInfo.setTotal(Convert.toInt(count));
        return pageInfo;
    }

    /**
     * 获取评论的消息列表的数据
     *
     * @param page
     * @param pageSize
     * @param like
     * @param userId
     * @return
     */
    @Override
    public List<Comment> messagesAllComment(Integer page, Integer pageSize, CommentType like, Long userId) {
        List<Comment> commentList = null;
        PageRequest pageRequest = PageRequest.of(page, pageSize, Sort.by(Sort.Order.desc("created")));
        Query query = Query.query(Criteria.where("publishUserId").is(userId).and("commentType").is(like.getType())).with(pageRequest);
        commentList = mongoTemplate.find(query, Comment.class);
        return commentList;
    }

    /**
     * 取消点赞的comment
     *
     * @param userId
     * @param publishId
     * @param like
     * @return
     */
    private Boolean deleteLikeAndLoveAndComment(Long userId, String publishId, CommentType like) {
        //TODO
        Query query = Query.query(Criteria.where("userId").is(userId).and("publishId").is(new ObjectId(publishId)).and("commentType").is(like.getType()));
        return mongoTemplate.remove(query, Comment.class).getDeletedCount() > 0;
    }


    /**
     * 实现功能:根据发布id获取发布的对象id:例如小视频或者动态
     *
     * @param publishId
     * @param type
     * @return
     */
    public Long getPublish(String publishId, IdType type) {
        if (type == IdType.PUBLISH) {
            Query query = Query.query(Criteria.where("id").is(new ObjectId(publishId)));
            return mongoTemplate.findOne(query, Publish.class).getUserId();
        } else if (type == IdType.VIDEO) {
            Query query = Query.query(Criteria.where("id").is(new ObjectId(publishId)));
            return mongoTemplate.findOne(query, Video.class).getUserId();
        } else if (type == IdType.COMMENT) {
            Query query = Query.query(Criteria.where("id").is(new ObjectId(publishId)));
            return mongoTemplate.findOne(query, Comment.class).getUserId();
        } else {
            return null;
        }
    }


}
